﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using Styx.WoWInternals.DBC;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals;
using Styx.Common;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx;
using System.Diagnostics;
using System.Threading;
using System.IO;
using Styx.CommonBot;
using System.Windows.Forms;
using Styx.TreeSharp;

namespace KingWoW
{
    public class KingWoWUtility
    {
        public LocalPlayer Me { get { return Styx.StyxWoW.Me; } }
        public WoWUnit lastTarget = null;
        public WoWPlayer tank = null;
        public WoWPlayer lastTank = null;
        public string[,] parties = new string[5, 8];

        HealInterruptDispellsCleanse_Manager healInterrDispClea_Manager = null;
        
       
        public Stopwatch parties_sw;  //for update party or raid
        public Stopwatch tank_sw;     //for update tank
        
        public const int PW_BARRIER_RADIUS = 7;
        public const int HW_SANCTUARY_RADIUS = 8;
        public const int POM_RADIUS = 20;
        public const int COH_RADIUS = 30;
        public const int HEALING_RAIN_RADIUS = 10;
        public const int CHAIN_HEAL_RADIUS = 40;

        public WoWItem FindFirstBagItemByName(string name)
        {
            List<WoWItem> carried = StyxWoW.Me.CarriedItems;
            
            return (from i in carried
                    where i.Name.Equals(name)
                    select i).FirstOrDefault();
        }

        public void RunMacroText(string macro)
        {
            Lua.DoString("RunMacroText(\"" + RealLuaEscape(macro) + "\")");
        }

        public string RealLuaEscape(string luastring)
        {
            var bytes = Encoding.UTF8.GetBytes(luastring);
            return bytes.Aggregate(String.Empty, (current, b) => current + ("\\" + b));
        }

        public bool UseBagItem(string name)
        {
            WoWItem item = null;
            item = Me.BagItems.FirstOrDefault(x => x.Name == name && x.Usable && x.Cooldown <= 0);
            if( item != null)
            {
                LogActivity("Using " + name);
                item.UseContainerItem();
                return true;
            }
            return false;
        }

        
        public uint PlayerCountBuff(string name)
        {
            return GetAuraStack(Me, name, true);
        }

        public bool CanCastPetAction(string action)
        {
            WoWPetSpell petAction = StyxWoW.Me.PetSpells.FirstOrDefault(p => p.ToString() == action);
            if (petAction == null || petAction.Spell == null)
            {
                return false;
            }

            return !petAction.Spell.Cooldown;
        }

        public IEnumerable<WoWPlayer> NearbyFriendlyPlayersInRange(float range)
        {
            return ObjectManager.GetObjectsOfType<WoWPlayer>(true, true).Where(p => p.DistanceSqr <= range * range && p.IsFriendly).ToList();
        }

        public void CastPetAction(string action)
        {
            WoWPetSpell spell = StyxWoW.Me.PetSpells.FirstOrDefault(p => p.ToString() == action);
            if (spell == null)
                return;

            Lua.DoString("CastPetAction({0})", spell.ActionBarIndex + 1);
        }

        public bool MeIsCastingWithLag()
        {
            if (Me.CurrentCastTimeLeft.Milliseconds <= StyxWoW.WoWClient.Latency)
                return false;
            else
                return true;
        }

        public double MyAuraTimeLeft(string auraName, WoWUnit u)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                if (u == null || !u.IsValid || !u.IsAlive)
                    return 0;

                WoWAura aura = u.GetAllAuras().FirstOrDefault(a => a.Name == auraName && a.CreatorGuid == Me.Guid);

                if (aura == null)
                {
                    return 0;
                }
                return aura.TimeLeft.TotalMilliseconds;
            }
        }

        public bool HaveManaGem()
        {
            return ObjectManager.GetObjectsOfType<WoWItem>(false).Any(item => item.Entry == 36799 || item.Entry == 81901);
        }

        public int HowManyManaGem()
        {
            //return ObjectManager.GetObjectsOfType<WoWItem>(false).Any(item => item.Entry == 36799 || item.Entry == 81901);
            return ObjectManager.GetObjectsOfType<WoWItem>(false).Count(item => item.Entry == 36799 || item.Entry == 81901);
        }

        public bool MeIsChanneling { get { return StyxWoW.Me.ChanneledCastingSpellId != 0 && StyxWoW.Me.IsChanneling; } }

        public int COHRadius()
        {
            return COH_RADIUS;
        }

        public int PW_SanctuaryRadius()
        {
            return HW_SANCTUARY_RADIUS;
        }

        public int HEALING_RAIN_Radius()
        {
            return HEALING_RAIN_RADIUS;
        }

        public int POM_Radius()
        {
            return POM_RADIUS;
        }

        public int CHAIN_HEAL_Radius()
        {
            return CHAIN_HEAL_RADIUS;
        }

        public uint GetAuraStack(WoWUnit unit, string auraName, bool fromMyAura)
        {
            if (unit != null)
            {
                var wantedAura = unit.GetAllAuras().FirstOrDefault(a => a.Name == auraName && a.StackCount > 0 && (!fromMyAura || a.CreatorGuid == Me.Guid));
                return wantedAura != null ? wantedAura.StackCount : 0;
            }

            LogActivity(" [GetAuraStack] Unit is null ");
            return 0;
        }

        public bool HasAura(WoWUnit unit, int spellId, WoWUnit creator)
        {
            return unit != null && unit.GetAllAuras().Any(a => a.SpellId == spellId && (creator == null || a.CreatorGuid == creator.Guid));
        }

        public bool HasAura(WoWUnit unit, string aura, WoWUnit creator)
        {
            return unit != null && unit.GetAllAuras().Any(a => a.Name == aura && (creator == null || a.CreatorGuid == creator.Guid));
        }

        public bool PlayerHasBuff(string name)
        {
            return HasAura(Me, name, null);
        }

        public IEnumerable<WoWUnit> EnemyMeleeUnits
        {
            get
            {

                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                    {
                        var ret = ObjectManager.GetObjectsOfType<WoWUnit>(true, false)
                                  .Where(unit =>
                                         !unit.IsFriendly
                                         && (unit.IsTargetingMeOrPet
                                             || unit.IsTargetingMyPartyMember
                                             || unit.IsTargetingMyRaidMember
                                             || unit.IsPlayer
                                             || unit.MaxHealth == 1)
                                         && !unit.IsNonCombatPet
                                         && !unit.IsCritter
                                         && unit.DistanceSqr
                                         <= 12 * 12).OrderBy(u => u.DistanceSqr);

                        return ret;
                    }
                }
                catch (NullReferenceException)
                {
                    return new List<WoWUnit>();
                }
            }
        }

        public void printTargetParticulars(WoWUnit target,double rangeCk)
        {
            if (target == null)
                Logging.Write("NULL");
            else if (!target.InLineOfSight)
                Logging.Write("NOT in LOS");
            else if (target.Distance2DSqr > rangeCk*rangeCk)
                Logging.Write("NOT in "+rangeCk+" Range");
            else if (target.IsDead)
                Logging.Write("DEAD");
            else
                Logging.Write("OK");

        }

        public WoWUnit getTargetToAttack(double range,WoWUnit tank_passed)
        {
            WoWUnit target = Me.CurrentTarget;
            if (target == null || target.IsFriendly || !target.Attackable || !target.InLineOfSight || target.Distance2DSqr > range*range || target.IsDead)                
            {
                if (tank_passed != null && tank_passed.IsAlive)
                {
                    target = tank_passed.CurrentTarget;
                    if (target == null || target.IsFriendly || !target.Attackable || !target.InLineOfSight || target.Distance2DSqr > range * range || target.IsDead)                        
                    {
                        target = NearestAttaccableEnemyMobInRangeTargettingMyParty(range);
                    }
                    return target;

                }
                target = NearestAttaccableEnemyMobInRangeTargettingMyParty(range);
            }
            return target;
        }

        /*public int CountEnnemiesInRange(WoWPoint fromLocation, double maxRange)
        {
            return NearbyUnits(fromLocation, maxRange).Count;
        }

        private List<WoWUnit> NearbyUnits(WoWPoint fromLocation, double radius)
        {
            List<WoWUnit> hostile = ObjectManager.GetObjectsOfType<WoWUnit>(true, false);
            using (StyxWoW.Memory.AcquireFrame())
            {
                hostile = hostile.Where(x => !x.IsPlayer && x.Attackable && x.IsHostile && x.IsAlive && x.Location.Distance(fromLocation) < radius).ToList();
                return hostile;
            }
        }*/

        public IEnumerable<WoWUnit> AllAttaccableEnemyMobsInRange (double range)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return AllEnemyMobs.Where(u =>
                        (u.Distance2DSqr < range*range && u.InLineOfSight && u.Attackable));
            }

        }

        public IEnumerable<WoWUnit> AllAttaccableEnemyMobsInRangeFromTarget(WoWUnit target, double range)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return AllEnemyMobs.Where(u =>
                        (u.Location.Distance2DSqr(target.Location) < range*range && u.InLineOfSight && u.Attackable));
            }

        }

        public IEnumerable<WoWUnit> AllAttaccableEnemyMobsInRangeTragettingMyParty(double range)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return AllEnemyMobs.Where(u =>
                        (u.Distance2DSqr < range*range && u.InLineOfSight && u.Attackable && !u.IsPet && 
                        (u.IsTargetingMeOrPet || u.IsTargetingMyPartyMember || u.IsTargetingMyRaidMember)));
            }

        }

        public WoWUnit NearestAttaccableEnemyMobInRangeTargettingMyParty(double range)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return AllAttaccableEnemyMobsInRangeTragettingMyParty(range).OrderBy(uu => uu.Distance2DSqr).FirstOrDefault();
            }
        }

        public WoWUnit NearestAttaccableEnemyMobInRange(double range)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return AllAttaccableEnemyMobsInRange(range).OrderBy(uu => uu.Distance2DSqr).FirstOrDefault();
            }
        }

        public bool IsGlobalCooldown(bool allowLagTollerance = true)
        {
            uint latency = allowLagTollerance ? StyxWoW.WoWClient.Latency*2 : 0;
            TimeSpan gcdTimeLeft = SpellManager.GlobalCooldownLeft;
            //Logging.Write("Waiting............. latency="+latency+" GCD left: "+ gcdTimeLeft);
            //if (!Me.IsDead && !Me.IsMoving && Me.Combat && !Me.IsCasting && (gcdTimeLeft.TotalMilliseconds < latency))
            //    Logging.Write("Il dolce far niente! latency=" + latency);
            return gcdTimeLeft.TotalMilliseconds > latency;
        }

        public bool IsBotBaseInUse(string botname)
        {
            return BotManager.Current.Name == botname || BotManager.Current.Name.StartsWith(botname);
        }

        public IEnumerable<WoWUnit> AllEnemyMobsAttackingMe
        {
            get
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    return AllEnemyMobs.Where(u =>
                        (u.CurrentTargetGuid == Me.Guid || Me.CurrentTargetGuid == u.Guid)
                        && !u.IsPet
                        && StyxWoW.Me.Level < (u.Level + (u.Elite ? 12 : 5)));
                }
            }
        }

        public bool IsEnemy(WoWUnit u)
        {
            if (u == null || !u.CanSelect || !u.Attackable || !u.IsAlive || u.IsNonCombatPet || u.IsCritter)
                return false;

            if (!u.IsPlayer)
                return u.IsHostile || u.Aggro || u.PetAggro;

            WoWPlayer p = u.ToPlayer();
            /* // not supported currently
                        if (Battlegrounds.IsInsideBattleground)
                            return p.BattlefieldArenaFaction != Me.BattlefieldArenaFaction;
            */
            return p.IsHorde != StyxWoW.Me.IsHorde;
        }

        public IEnumerable<WoWUnit> AllEnemyMobs
        {
            get
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    return ObjectManager.ObjectList.Where(o => o is WoWUnit && IsEnemy(o.ToUnit())).Select(o => o.ToUnit()).ToList();
                }
            }
        }

        public WoWUnit NearestEnemyMobAttackingMe
        {
            get
            {
                return AllEnemyMobsAttackingMe.OrderBy(uu => uu.Distance2DSqr).FirstOrDefault();
            }
        } 
        
        public KingWoWUtility()
        {
            if(parties_sw == null)
                parties_sw=new Stopwatch();
            if (tank_sw == null)
                tank_sw = new Stopwatch();
            if (healInterrDispClea_Manager == null)
                healInterrDispClea_Manager = new HealInterruptDispellsCleanse_Manager();
        }

        public bool NeedsCure(WoWPlayer player)
        {
            foreach (WoWAura a in player.Auras.Values)
            {
                if (a.IsHarmful)
                {
                    WoWDispelType t = a.Spell.DispelType;
                    if (t == WoWDispelType.Poison) return true;
                }
            }

            return false;
        }

        public bool NeedsDispel(WoWPlayer player)
        {
            foreach (WoWAura a in player.Auras.Values)
            {
                if (a.IsHarmful && Me.ManaPercent > 35)
                {
                    WoWDispelType t = a.Spell.DispelType;
                    if (t == WoWDispelType.Magic) return true;
                }
            }

            return false;
        }

        public int GetMemberCountBelowThreshold(int threshold)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return Enumerable.Count((from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                                         orderby unit.HealthPercent ascending
                                         where !unit.IsDead
                                         where (unit.IsInMyPartyOrRaid || unit.IsMe)
                                         where !unit.IsGhost
                                         where unit.HealthPercent <= threshold
                                         select unit));
            }
        }

        public int GetMemberCountBelowThresholdInRangeFromPlayer(int threshold, WoWUnit from_player, double range)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                if (from_player != null)
                    return Enumerable.Count((from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                                             orderby unit.HealthPercent ascending
                                             where !unit.IsDead
                                             where (unit.IsInMyPartyOrRaid || unit.IsMe)
                                             where !unit.IsGhost
                                             where unit.HealthPercent <= threshold
                                             where GetDistance(unit.Location, from_player.Location) < range
                                             select unit));
            }
            return 0;
        }

        public WoWPlayer GetHealTarget(bool lockframe)
        {
            if (lockframe)
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    return (from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                            orderby unit.HealthPercent ascending
                            where (unit.IsInMyPartyOrRaid || unit.IsMe)
                            where !(unit.HasAura("Deep Corruption") && unit.Auras["Deep Corruption"].StackCount == 4)
                            where !healInterrDispClea_Manager.TargetDontHeal(unit)
                            where !unit.IsDead
                            where !unit.IsGhost
                            where unit.IsFriendly
                            where unit.Distance2DSqr < 40*40
                            where unit.InLineOfSight
                            select unit).FirstOrDefault();
                }
            }
            else
            {
                return (from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                        orderby unit.HealthPercent ascending
                        where (unit.IsInMyPartyOrRaid || unit.IsMe)
                        where !(unit.HasAura("Deep Corruption") && unit.Auras["Deep Corruption"].StackCount == 4)
                        where !unit.IsDead
                        where !unit.IsGhost
                        where unit.IsFriendly
                        where unit.Distance2DSqr < 40*40
                        where unit.InLineOfSight
                        select unit).FirstOrDefault();
            }
        }

        public IEnumerable<WoWPlayer> GetResurrectTargets()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return (from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(false, false)
                        orderby unit.MaxHealth descending
                        where unit.IsDead
                        where unit.IsInMyPartyOrRaid
                        where !unit.IsGhost
                        where unit.InLineOfSight
                        where unit.Distance2DSqr <= 40*40
                        select unit);
            }
        }

        public WoWPlayer GetCureOrDispellTarget()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return (from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                        orderby unit.HealthPercent ascending
                        where !unit.IsDead
                        where !unit.IsGhost
                        where unit.Distance2DSqr <= 40*40
                        where unit.InLineOfSight
                        where (NeedsCure(unit) || NeedsDispel(unit))
                        where (unit.IsInMyPartyOrRaid || unit.IsMe)
                        select unit).FirstOrDefault();
            }
        }

        public WoWPlayer GetCureTarget()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return (from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                        orderby unit.HealthPercent ascending
                        where !unit.IsDead
                        where !unit.IsGhost
                        where unit.Distance2DSqr <= 40*40
                        where unit.InLineOfSight
                        where NeedsCure(unit)
                        where (unit.IsInMyPartyOrRaid || unit.Guid == Me.Guid)
                        select unit).FirstOrDefault();
            }
        }

        public WoWPlayer GetDispelTarget()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return (from unit in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                        orderby unit.HealthPercent ascending
                        where !unit.IsDead
                        where !unit.IsGhost
                        where unit.Distance2DSqr <= 40*40
                        where unit.InLineOfSight
                        where NeedsDispel(unit)
                        where (unit.IsInMyPartyOrRaid || unit.Guid == Me.Guid)
                        select unit).FirstOrDefault();
            }
        }

        public int MassDispelCountForPlayer(WoWPlayer player)
        {
            int count = 0;
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.IsInMyPartyOrRaid && GetDistance(player.Location, p.Location) <= 15 && NeedsDispel(p))
                        count++;
                }
                return count;
            }
        }

        public bool isAuraActive(string name)
        {
            return isAuraActive(name, Me);
        }

        public bool isAuraActive(string name, WoWUnit unit)
        {
            return unit.Auras.ContainsKey(name);
        }

        public void LogActivity(params string[] words)
        {
            Logging.Write(DateTime.Now.ToString("ss:fff") + " my HP% " + Math.Round(Me.HealthPercent, 2) +
            "    MANA% " + Math.Round(Me.ManaPercent, 2) + "     " + String.Join(": ", words));
        }

        public void LogHealActivity(WoWUnit unit,params string[] words)
        {
            Logging.Write(DateTime.Now.ToString("ss:fff") + "target   HP%=" + Math.Round(unit.HealthPercent,2) + "   " + String.Join(": ", words));
        }

        public bool CanCast(string spell, bool range_ck)
        {
            return SpellManager.CanCast(spell, range_ck);
        }


        public bool CanCast(string spell, WoWUnit target)
        {
            return SpellManager.CanCast(spell, target);
        }

        public bool CanCast(string spell, WoWUnit target, bool range_ck)
        {
            return SpellManager.CanCast(spell, target, range_ck);
        }

        public bool CanCast(string spell)
        {
            return SpellManager.CanCast(spell);
        }

        public bool Cast(string spell, WoWUnit target)
        {
            if (SpellManager.Cast(spell, target))
            {
                lastTarget = target;
                return true;
            }

            return false;
        }

        public TimeSpan GetSpellCooldown(string spell)
        {
            SpellFindResults sfr;
            if (SpellManager.FindSpell(spell, out sfr))
                return (sfr.Override ?? sfr.Original).CooldownTimeLeft;

            return TimeSpan.MaxValue;
        }

        public bool Cast(string spell)
        {
            lastTarget = Me;
            return SpellManager.Cast(spell, Me);
        }

        public WoWUnit FirstplayerHasAggroInRange(float range)
        {
            //using (StyxWoW.Memory.AcquireFrame())
            //{
                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
                {
                    if (u.IsTargetingMyPartyMember && u.IsAlive && !u.IsFriendly && u.Combat && u.CurrentTarget.InLineOfSight
                        && u.CurrentTarget.IsAlive)
                    {
                        return u.CurrentTarget;
                    }
                }
            //}
            return null;

        }

        public bool PlayerHasAggro(WoWPlayer p)
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
                {
                    if (u.IsTargetingMyPartyMember && u.IsAlive && !u.IsFriendly && u.Combat && (u.CurrentTarget.Name.Equals(p.Name)))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public bool GroupNeedsMyAura(string aura)
        {
            if (StyxWoW.Me.HasAura(aura))
            {
                return false;
            }
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.IsInMyPartyOrRaid && p.HasAura(aura) && p.Auras[aura].CreatorGuid == StyxWoW.Me.Guid)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public WoWPlayer GetTank()
        {
            UpdateTank();
            return tank;
        }

        public string DeUnicodify(string spell)
        {
            StringBuilder sb = new StringBuilder();
            byte[] bytes = Encoding.UTF8.GetBytes(spell);

            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (byte b in bytes)
                {
                    if (b != 0)
                        sb.Append("\\" + b);
                }
            }

            return sb.ToString();
        }

        public bool IsTank(WoWPlayer player)
        {
            return Lua.GetReturnValues("return UnitGroupRolesAssigned('" + DeUnicodify(player.Name) + "')").First() == "TANK";
        }

        public bool IsHealer(WoWPlayer player)
        {
            return Lua.GetReturnValues("return UnitGroupRolesAssigned('" + DeUnicodify(player.Name) + "')").First() == "HEALER";
        }

        public void ResetParties()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                for (int player_number = 0; player_number < 5; player_number++)
                {
                    for (int group_number = 0; group_number < 8; group_number++)
                    {
                        parties[player_number, group_number] = " ";
                    }
                }
            }
        }

        public void FillParties()
        {
            int currentPlayer = 0;
            int currentParty = 0;

            ResetParties();

            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.IsInMyPartyOrRaid)
                    {
                        if (currentPlayer > 4 && currentParty > 8)
                        {
                            Logging.Write("ERROR in FillParties!!!");
                            ResetParties();
                            return;
                        }
                        if (currentPlayer > 4)
                        {
                            currentParty++;
                            currentPlayer = 0;
                        }
                        if (currentPlayer <= 4)
                        {
                            parties[currentPlayer, currentParty] = p.Name;
                            currentPlayer++;
                        }
                    }
                }
            }
        }

        public int GetPlayerParty(WoWPlayer p)
        {
            int party = 0;
            //using (StyxWoW.Memory.AcquireFrame())
            //{
                for (int player_number = 0; player_number < 5; player_number++)
                {
                    for (int group_number = 0; group_number < 8; group_number++)
                    {
                        if (p.Name.Equals(parties[player_number, group_number]))
                        {
                            return party;
                        }
                    }
                    party++;
                }
            //}
            return -1;
        }

        public int GetDistance(WoWPoint p1, WoWPoint p2)
        {
            double XDif = Math.Pow((p2.X - p1.X), 2);
            double YDif = Math.Pow((p2.Y - p1.Y), 2);
            //double ZDif = Math.Pow((p2.Z - p1.Z), 2);
            int distance = (int)Math.Sqrt(XDif + YDif);

            return distance;
        }

        public int AOEHealCount(bool lockframe,WoWPlayer player, int hp, int dist)
        {
            int count = 0;
            if (lockframe)
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                    {
                        if ((p.IsInMyPartyOrRaid || p.IsMe)
                            && GetDistance(player.Location, p.Location) <= dist
                            && p.IsAlive
                            && p.HealthPercent <= hp)
                        {
                            count++;
                        }
                    }
                }
            }
            else
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if ((p.IsInMyPartyOrRaid || p.IsMe)
                        && GetDistance(player.Location, p.Location) <= dist
                        && p.IsAlive
                        && p.HealthPercent <= hp)
                    {
                        count++;
                    }
                }
            }
            return count;
        }

        public int GetPartyAOECount(WoWPlayer player, int hp)
        {
            int count = 0;
            int playerParty;
            if (player.IsInMyParty)
            {
                //using (StyxWoW.Memory.AcquireFrame())
                //{
                    foreach (WoWPlayer p in StyxWoW.Me.PartyMembers)
                    {
                        if (GetDistance(player.Location, p.Location) < 30
                            && p.IsAlive
                            && p.HealthPercent <= hp)
                        {
                            count++;
                        }
                    }
                //}
            }
            else
            {
                playerParty = GetPlayerParty(player);
                //using (StyxWoW.Memory.AcquireFrame())
                //{
                    foreach (WoWPlayer p in StyxWoW.Me.RaidMembers)
                    {
                        if (GetPlayerParty(p) == playerParty
                            && p.IsAlive
                            && p.Distance2DSqr < 30*30
                            && p.HealthPercent <= hp)
                        {
                            count++;
                        }
                    }
                //}
            }
            return count;
        }

        public WoWPlayer NextSpiritShieldTarget(string SPIRIT_SHELL_SPELL_NAME)
        {
            if (Me.CurrentTarget != null && Me.CurrentTarget.IsFriendly && Me.CurrentTarget.IsAlive && Me.CurrentTarget.IsPlayer
                && Me.CurrentTarget.Distance2DSqr < 40*40)
                return Me.CurrentTarget.ToPlayer();
            //using (StyxWoW.Memory.AcquireFrame())
            //{
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && p.IsInMyPartyOrRaid
                        && (!p.HasAura(SPIRIT_SHELL_SPELL_NAME) || p.Auras[SPIRIT_SHELL_SPELL_NAME].CreatorGuid != StyxWoW.Me.Guid))
                    {
                        return p;
                    }
                }
            //}
            return null;
        }

        public WoWPlayer BestPoHTarget(int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && GetPartyAOECount(p, hp) > GetPartyAOECount(tar, hp))
                    {
                        tar = p;
                    }
                }

                if (GetPartyAOECount(tar, hp) >= count)
                {
                    return tar;
                }
                else
                {
                    return null;
                }
            }
        }

        public WoWPlayer BestPWBTargetLocation(int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && AOEHealCount(false,p, hp, PW_BARRIER_RADIUS) > AOEHealCount(false,tar, hp, PW_BARRIER_RADIUS))
                    {
                        tar = p;
                    }
                }
            }
            if (AOEHealCount(true,tar, hp, PW_BARRIER_RADIUS) >= count)
            {
                return tar;
            }
            else
            {
                return null;
            }
        }

        public WoWPlayer BestHealingRainTargetLocation(bool lockframe, int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            if (lockframe)
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                    {
                        if (p.Distance2DSqr < 40*40
                            && p.IsAlive
                            && p.InLineOfSight
                            && AOEHealCount(false, p, hp, HEALING_RAIN_RADIUS) > AOEHealCount(false, tar, hp, HEALING_RAIN_RADIUS))
                        {
                            tar = p;
                        }
                    }
                }
            }
            else
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && AOEHealCount(false, p, hp, HEALING_RAIN_RADIUS) > AOEHealCount(false, tar, hp, HEALING_RAIN_RADIUS))
                    {
                        tar = p;
                    }
                }
            }
            if (AOEHealCount(lockframe, tar, hp, HEALING_RAIN_RADIUS) >= count)
            {
                return tar;
            }
            else
            {
                return null;
            }
        }


        public WoWPlayer BestHWSanctuaryTargetLocation(bool lockframe,int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            if (lockframe)
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                    {
                        if (p.Distance2DSqr < 40*40
                            && p.IsAlive
                            && p.InLineOfSight
                            && AOEHealCount(false, p, hp, HW_SANCTUARY_RADIUS) > AOEHealCount(false, tar, hp, HW_SANCTUARY_RADIUS))
                        {
                            tar = p;
                        }
                    }
                }
            }
            else
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && AOEHealCount(false, p, hp, HW_SANCTUARY_RADIUS) > AOEHealCount(false, tar, hp, HW_SANCTUARY_RADIUS))
                    {
                        tar = p;
                    }
                }
            }
            if (AOEHealCount(lockframe,tar, hp, HW_SANCTUARY_RADIUS) >= count)
            {
                return tar;
            }
            else
            {
                return null;
            }
        }

        public WoWPlayer BestPOM_PROC_DIVINE_INSIGHT_Target(int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && AOEHealCount(false,p, hp, POM_RADIUS) > AOEHealCount(false,tar, hp, POM_RADIUS))
                    {
                        tar = p;
                    }
                }
            }
            if (AOEHealCount(true,tar, hp, POM_RADIUS) >= count)
            {
                return tar;
            }
            else
            {
                return null;
            }
        }

        public WoWPlayer BestCircleOfHealing_Target(int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && AOEHealCount(false,p, hp, COH_RADIUS) > AOEHealCount(false,tar, hp, COH_RADIUS))
                    {
                        tar = p;
                    }
                }
            }
            if (AOEHealCount(true,tar, hp, COH_RADIUS) >= count)
            {
                return tar;
            }
            else
            {
                return null;
            }
        }

        public WoWPlayer BestChaiHeal_Target(int hp, int count)
        {
            WoWPlayer tar = StyxWoW.Me;
            using (StyxWoW.Memory.AcquireFrame())
            {
                foreach (WoWPlayer p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true))
                {
                    if (p.Distance2DSqr < 40*40
                        && p.IsAlive
                        && p.InLineOfSight
                        && AOEHealCount(false, p, hp, CHAIN_HEAL_RADIUS) > AOEHealCount(false, tar, hp, CHAIN_HEAL_RADIUS))
                    {
                        tar = p;
                    }
                }
            }
            if (AOEHealCount(true, tar, hp, CHAIN_HEAL_RADIUS) >= count)
            {
                return tar;
            }
            else
            {
                return null;
            }
        }

        public void LogParties()
        {
            for (int player_number = 0; player_number < 5; player_number++)
            {
                for (int group_number = 0; group_number < 8; group_number++)
                {
                    Logging.Write(parties[player_number, group_number] + " in party: " + group_number);
                }
            }
        }

        public bool InRaid()
        {
            return Me.RaidMembers.Count > 0;
        }

        public WoWUnit SimpleGetTank()
        {
            //non critical dont use framelock
            WoWUnit focused_unit = Me.FocusedUnit;
            if (focused_unit != null && focused_unit.IsPlayer && focused_unit.InLineOfSpellSight && focused_unit.Distance2DSqr < 40*40)
            {
                return focused_unit;
            }
            else
            {
                if (Me.GroupInfo.IsInRaid)
                {
                    foreach (WoWPlayer player in Me.RaidMembers)
                    {
                        if (IsTank(player) && player.InLineOfSpellSight && player.Distance2DSqr < 40*40)
                        {
                            return player;
                        }
                    }
                    return FirstplayerHasAggroInRange(40);
                }
                else if (Me.GroupInfo.IsInParty)
                {

                    foreach (WoWPlayer player in Me.PartyMembers)
                    {
                        if (IsTank(player))
                        {
                            return player;
                        }
                    }
                    return FirstplayerHasAggroInRange(40);
                }
            }
            return null;
        }

        public void UpdateTank()
        {
            if (tank_sw.Elapsed.TotalSeconds > 2)
            {
                tank_sw.Reset();
                tank_sw.Start();
                //non critical dont use framelock
                WoWUnit focused_unit = Me.FocusedUnit;
                if (focused_unit != null && focused_unit.IsPlayer)
                {
                    tank = focused_unit.ToPlayer();
                    return;
                }
                else
                {
                    if (Me.GroupInfo.IsInRaid)
                    {
                        foreach (WoWPlayer player in Me.RaidMembers)
                        {
                            if (IsTank(player))
                            {
                                tank = player;
                                return;
                            }
                        }
                    }
                    else if (Me.GroupInfo.IsInParty)
                    {

                        foreach (WoWPlayer player in Me.PartyMembers)
                        {
                            if (IsTank(player))
                            {
                                tank = player;
                            }
                        }
                    }
                }
            }
        }

        public void UpdateParties()
        {
            if (InRaid() && parties_sw.Elapsed.TotalSeconds > 15)
            {
                FillParties();
                parties_sw.Reset();
                parties_sw.Start();
                //LogParties();
                //Logging.Write("Parties updated");
            }
        }

        public bool Mounted()
        {
            return Me.Mounted;
        }

        public void GoStopWatchUpdateParty()
        {
            if (parties_sw != null)
            {
                parties_sw.Start();
            }
        }

        public void InterruptStopWatchUpdateParty()
        {
            if (parties_sw != null)
            {
                parties_sw.Stop();
            }
        }

        public void GoStopWatchUpdateTank()
        {
            if (tank_sw != null)
            {
                tank_sw.Start();
            }
        }

        public void InterruptStopWatchUpdateTank()
        {
            if (tank_sw != null)
            {
                tank_sw.Stop();
            }
        }


        

     }
}
